﻿namespace Knownet.Utils
{
  using System;
  using System.IO;
  using System.Text;

  /// <summary>
  /// 标记可回溯流阅读器。
  /// </summary>
  public class MarkingStreamReader : MarkingTextReader
  {
    #region Statics
    #endregion
    #region Fields
    protected StreamReader strReader = null;
    #endregion
    #region Properties
    public override bool CanRead
    {
      get
      {
        return (!this.IsDisposed) ? !this.strReader.EndOfStream : false;
      }
    }

    public override MarkingTextReader Duplicate()
    {
      if (!this.IsDisposed)
      {
        return new MarkingStreamReader(this.strReader);
      }
      return null;
    }

    public override bool IsDisposed
    {
      get { return this.strReader == null; }
    }

    public virtual Encoding Encoding
    {
      get
      {
        return this.strReader.CurrentEncoding;
      }
    }
    #endregion
    #region Constructors
    public MarkingStreamReader(Stream s):this(new StreamReader(s)){}
    public MarkingStreamReader(string filepath):this(new StreamReader(filepath))
    {

    }
    public MarkingStreamReader(StreamReader reader)
    {
      if (reader == null) throw new ArgumentNullException("reader", "reader can not be null!");
      if (!reader.BaseStream.CanSeek) throw new ArgumentException("reader is not seekable", "reader");
      this.strReader = reader;
    }
    #endregion
    #region Methods
    public override void Reset()
    {
      base.Reset();
      this.strReader.BaseStream.Seek(0L, SeekOrigin.Begin);
    }
    public override int Peek()
    {
      return this.strReader.Peek();
    }
    public override char Peekchar()
    {
      return (char)this.Peek();
    }
    public override int Read()
    {
      int val = this.strReader.Read();
      this.Encounter((char)val);
      return val;
    }
    public override char Readchar()
    {
      return this.Encounter((char)this.strReader.Read());
    }
  
    public override int Read(char[] buffer, int index, int count)
    {
      int c= this.strReader.Read(buffer, index, count);
      this.Encounter(buffer, c);
      return c;
    }
    public override int ReadBlock(char[] buffer, int index, int count)
    {
      int c =this.strReader.ReadBlock(buffer, index, count);
      this.Encounter(buffer, c);
      return c;
    }

    public override string ReadText(ref int length)
    {
      if (length <= 0) throw new ArgumentException("length <=0 is not allowed!");

      char[] buffer = new char[length];

      this.Read(buffer, 0, length);

      return new string(buffer);
    }

    public override string ReadLine()
    {
      return this.Encounter(this.strReader.ReadLine());
    }
    public override string ReadToEnd()
    {
      return this.Encounter(this.strReader.ReadToEnd());
    }

    protected virtual char Encounter(char ch)
    {
      this.Relocate(ch);
      this.position += this.CountBytes(ch);
      return ch;
    }
    protected virtual int Encounter(char[] chars,int count)
    {
      this.Relocate(chars);
      this.position += this.CountBytes(count, chars);
      return count;
    }
    protected virtual string Encounter(string str)
    {
      this.Relocate(str);
      this.position += this.CountBytes(str);
      return str;
    }

    protected virtual int CountBytes(string str)
    {
      return (str != null) ? this.Encoding.GetByteCount(str) : 0;
    }
    protected virtual int CountBytes(params char[] chars)
    {
      return (chars != null) ?this.CountBytes(chars.Length, chars):0;
    }
    protected virtual int CountBytes(int count,params char[] chars)
    {
      return (chars != null) ? this.Encoding.GetByteCount(chars, 0, count) : 0;
    }
    protected virtual void Relocate(char ch)
    {
      if (ch == '\r')
      {
        ch = this.Peekchar();
        if (ch == '\n')
        {
          this.strReader.Read();
          this.position ++ ;
        }
        location.IncreaseRow();
      }
      else if (ch == '\n')
      {
        this.location.IncreaseRow();
      }
      else
      {
        this.location.IncreaseChar(ch == '\t' ? this.tabCount : 1);
      }
    }
    public override int Recall()
    {
      int top = base.Recall();

      this.strReader.DiscardBufferedData();
      this.strReader.BaseStream.Position = this.position;

      return top;
    }

    protected override void Dispose(bool disposing)
    {
      if (disposing)
      {
        this.strReader.Close();
        this.strReader = null;
      }
    }
    #endregion
  }
}
